package au.edu.qut.bpm.genbinder.util;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

import au.edu.qut.bpm.genbinder.models.AbstractLog;
import au.edu.qut.bpm.genbinder.models.Activity;
import au.edu.qut.bpm.genbinder.models.Case;
import au.edu.qut.bpm.genbinder.models.ConcreteEventLog;

public class DiscreteLogConverter {
	// Converts a normal log to discrete one, using the same long representation
	// for time, but this will now express blocks instead of actual timestamp
	// This has the neat property that you can just use the same fitness
	// operators

	public static ConcreteEventLog makeDiscrete(ConcreteEventLog log, long blockZeroTime,
			long blockDuration) {
		AbstractLog newLog = new AbstractLog(log.getAbstractLog().getId());
		//AbstractLog newLog = new AbstractLog(log.getAbstractLog().getId() + "_d");

		newLog.getResources().addAll(log.getAbstractLog().getResources());
		newLog.getTasks().addAll(log.getAbstractLog().getTasks());
		newLog.getTaskResourceSetMap().putAll(log.getAbstractLog().getTaskResourceSetMap());

		Map<Activity, Activity> old2New = new HashMap<Activity, Activity>();
		for (Case ca : log.getAbstractLog().getCases()) {
			Case newCase = new Case(ca.getId(), newLog, timeToUpperBlock(ca.getArrival(), blockZeroTime, blockDuration),	ca.getAttributes());
			//Case newCase = new Case(ca.getId() + "_d", newLog, timeToUpperBlock(ca.getArrival(), blockZeroTime, blockDuration), ca.getAttributes());
			for (Activity ac : ca.getActivities()) {
				Activity newActivity = new Activity(ac.getId(), "name", newCase, ac.getTask(), ac.getStartTime(), durationToBlocks(ac.getDuration(), blockZeroTime, blockDuration), ac.getResource(), ac.getAttributes());
				//Activity newActivity = new Activity(ac.getId() + "_d", "name", newCase,	ac.getTask(), durationToBlocks(ac.getDuration(), blockZeroTime, blockDuration), ac.getResource(), ac.getAttributes());
				newCase.getActivities().add(newActivity);
				old2New.put(ac, newActivity);
			}
			for (Activity old : ca.getActivities()) {
				if (!ca.getPartialOrder().containsKey(old))
					continue;
				newCase.getPartialOrder().put(old2New.get(old), new HashSet<Activity>());
				for (Activity oldTo : ca.getPartialOrder().get(old))
					newCase.getPartialOrder().get(old2New.get(old)).add(old2New.get(oldTo));
			}
			newLog.getCases().add(newCase);
		}

		ConcreteEventLog newConcreteEventLog = new ConcreteEventLog(newLog);
		for (Activity old : old2New.keySet()) {
			newConcreteEventLog.setActivityStart(old2New.get(old),
					timeToLowerBlock(log.getActivityStart(old), blockZeroTime, blockDuration));
			newConcreteEventLog.setActivityResources(old2New.get(old), log.getActivityResources(old));
		}

		return newConcreteEventLog;
	}

	public static ConcreteEventLog makeDiscrete(AbstractLog log, long blockZeroTime,
			long blockDuration) {
		return makeDiscrete(new ConcreteEventLog(log), blockZeroTime, blockDuration);
	}

	public static long timeToLowerBlock(long time, long blockZeroTime,
			long blockDuration) {
		long t = time - blockZeroTime;
		return (long) Math.floor((double) t / (double) blockDuration);
	}

	public static long timeToUpperBlock(long time, long blockZeroTime,
			long blockDuration) {
		long t = time - blockZeroTime;
		return (long) Math.ceil((double) t / (double) blockDuration);
	}

	public static long durationToBlocks(long duration, long blockZeroTime,
			long blockDuration) {
		return (long) Math.ceil((double) duration / (double) blockDuration);
	}
}
